import { isObject, toRawType, def } from "@vue/shared";
import {
  mutableHandlers,
  readonlyHandlers,
  shallowReactiveHandlers,
  shallowReadonlyHandlers,
} from "./baseHandlers";
import {
  mutableCollectionHandlers,
  readonlyCollectionHandlers,
  shallowCollectionHandlers,
  shallowReadonlyCollectionHandlers,
} from "./collectionHandlers";
import { UnwrapRefSimple, Ref } from "./ref";

// enum TS枚举的使用
export const enum ReactiveFlags {
  SKIP = "__v_skip", // 响应式对象的表示 markRaw()方法会给对象加上 __v_skip=true 这个对象就不会是响应式对象
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
  RAW = "__v_raw",
}

// target接口类型
export interface Target {
  [ReactiveFlags.SKIP]?: boolean;
  [ReactiveFlags.IS_REACTIVE]?: boolean;
  [ReactiveFlags.IS_READONLY]?: boolean;
  [ReactiveFlags.RAW]?: any;
}

/**
 * reactiveMap: 依赖收集的一个关键集合 这里采用的是Map数据结构
 *  Map的好处：
 *    - Map默认不包含任何的key，只包含显示插入的key，而不像对象有一堆的原型链上的值
 *    - Map的key可以是任意值，函数、对象、基本类型，而对象的key只能是字符串或者Symbol
 *    - Map的key是有顺序的，迭代的时候按照插入的顺序进行迭代
 *    - Map类型提供了类似数组的size属性
 *    - Map内置迭代器，可以直接迭代，而对象迭代必须使用Object.key() Object.values() Object.entrys()
 *    - 频繁的增删改查的清空下，性能优秀
 *
 *  WeakMap与Map的区别
 *    - WeakMap对象中的键只能是对象引用（key只能是对象，其他的类型会报错）
 *    - WeakMap的key所引用的对象都是弱引用，只要对象的其他引用被删除，垃圾回收机制就会释放该对象占用的内存，从而避免内存泄漏。
 *    - 由于WeakMap的成员随时可能被垃圾回收机制回收，成员的数量不稳定，所以没有size属性。
 *    - 没有clear()方法
 *    - 不能遍历
 *
 *  这里之所以使用weakMap原因就在于WeakMap是弱引用，无论是读还是写都是更加节省性能
 *
 *  这个主要是收集 目标对象和转成proxy的映射关系
 */
export const reactiveMap = new WeakMap<Target, any>(); // 存储响应式对象的集合
export const shallowReactiveMap = new WeakMap<Target, any>();
export const readonlyMap = new WeakMap<Target, any>(); // 只读的 集合
/**
 * target源对象和target的代理proxy对象集合
 */
export const shallowReadonlyMap = new WeakMap<Target, any>();

const enum TargetType {
  INVALID = 0,
  COMMON = 1,
  COLLECTION = 2, // 无效类型
}

function targetTypeMap(rawType: string) {
  switch (rawType) {
    case "Object":
    case "Array":
      // 普通类型
      return TargetType.COMMON;
    case "Map":
    case "Set":
    case "WeakMap":
    case "WeakSet":
      // 集合类型
      return TargetType.COLLECTION;
    default:
      return TargetType.INVALID;
  }
}

// 获取目标对象类型的方法 返回对象类型
function getTargetType(value: Target) {
  /**
   * Object.isExtensible(obj) 判断obj是否是一个可扩展的对象，而不是冻结对象等等
   *  Object.freeze()可用于创建冻结对象
   */
  //
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID
    : targetTypeMap(toRawType(value));
}

// only unwrap nested ref
export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>;

/**
 * Creates a reactive copy of the original object.
 *
 * The reactive conversion is "deep"—it affects all nested properties. In the
 * ES2015 Proxy based implementation, the returned proxy is **not** equal to the
 * original object. It is recommended to work exclusively with the reactive
 * proxy and avoid relying on the original object.
 *
 * A reactive object also automatically unwraps refs contained in it, so you
 * don't need to use `.value` when accessing and mutating their value:
 *
 * ```js
 * const count = ref(0)
 * const obj = reactive({
 *   count
 * })
 *
 * obj.count++
 * obj.count // -> 1
 * count.value // -> 1
 * ```
 */
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;
export function reactive(target: object) {
  /**
   * 我们要确定的就是 reactive返回的是一个 Proxy
   */
  // if trying to observe a readonly proxy, return the readonly version.
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    // 如果传入的对象是代理对象，就可以直接返回代理对象
    return target;
  }
  // 不是代理对象  包装成一个  响应对象（Proxy）
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  );
}

export declare const ShallowReactiveMarker: unique symbol;

export type ShallowReactive<T> = T & { [ShallowReactiveMarker]?: true };

/**
 * Return a shallowly-reactive copy of the original object, where only the root
 * level properties are reactive. It also does not auto-unwrap refs (even at the
 * root level).
 */

/**
 * 非递归监听的方法
 *  传递的对象只会监听第一层 不会往深了进行监听，避免资源的浪费
 */
export function shallowReactive<T extends object>(
  target: T
): ShallowReactive<T> {
  return createReactiveObject(
    target,
    false,
    shallowReactiveHandlers, //shallow 的 Handlers 主要处理的就是不会深入递归的监听
    shallowCollectionHandlers,
    shallowReactiveMap
  );
}

type Primitive = string | number | boolean | bigint | symbol | undefined | null;
type Builtin = Primitive | Function | Date | Error | RegExp;
export type DeepReadonly<T> = T extends Builtin
  ? T
  : T extends Map<infer K, infer V>
  ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
  : T extends ReadonlyMap<infer K, infer V>
  ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
  : T extends WeakMap<infer K, infer V>
  ? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
  : T extends Set<infer U>
  ? ReadonlySet<DeepReadonly<U>>
  : T extends ReadonlySet<infer U>
  ? ReadonlySet<DeepReadonly<U>>
  : T extends WeakSet<infer U>
  ? WeakSet<DeepReadonly<U>>
  : T extends Promise<infer U>
  ? Promise<DeepReadonly<U>>
  : T extends Ref<infer U>
  ? Ref<DeepReadonly<U>>
  : T extends {}
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : Readonly<T>;

/**
 * Creates a readonly copy of the original object. Note the returned copy is not
 * made reactive, but `readonly` can be called on an already reactive object.
 */
export function readonly<T extends object>(
  target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
  return createReactiveObject(
    target,
    true,
    readonlyHandlers,
    readonlyCollectionHandlers,
    readonlyMap
  );
}

/**
 * Returns a reactive-copy of the original object, where only the root level
 * properties are readonly, and does NOT unwrap refs nor recursively convert
 * returned properties.
 * This is used for creating the props proxy object for stateful components.
 */
export function shallowReadonly<T extends object>(
  target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
  return createReactiveObject(
    target,
    true,
    shallowReadonlyHandlers, // 基础对象的处理
    shallowReadonlyCollectionHandlers, // 集合对象的处理
    shallowReadonlyMap // 代理集合
  );
}

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>, // 基础对象的处理
  collectionHandlers: ProxyHandler<any>, // 集合对象的处理
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {
    // 不是对象 不能被包装成一个响应式对象  reactiveAPI 传入的参数必须是一个对象
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`);
    }
    return target;
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object

  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    // target是原始对象 不是只读也不是响应式对象 直接返回
    return target;
  }
  // target already has corresponding Proxy
  /**
   * 尝试直接读取映射 ，如果读取成功说明已经是响应式对象了 直接返回这个Proxy对象即可
   *  proxyMap 存储的是 target源对象和target的代理proxy对象集合
   *    所以可以使用 Map API 来获取这个 target对象的 proxy对象集合
   */
  const existingProxy = proxyMap.get(target);
  if (existingProxy) {
    // 如果 代理集合中存在这个对象 则直接获取这个对象再直接返回
    return existingProxy;
  }
  // only a whitelist of value types can be observed.
  // 获取这个 目标对象的 类型  之后会根据这个类型处理不同的Proxy handler
  const targetType = getTargetType(target);

  if (targetType === TargetType.INVALID) {
    // 如果是无效对象 则不用继续包装了 直接返回即可
    return target;
  }

  /**
   * 前面的都是一些边界处理，这里是最重要的创建代理的步骤
   *  Proxy 基本API
   *    new Proxy(target,handler)
   *      handler 是对这个对象处理的方式
   *
   */
  const proxy = new Proxy(
    target,
    /**
     * 判断是集合类型还是普通类型  传递分别不同类型的处理对象
     */
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  );
  /**
   * proxyMap 存储的是  target原对象 和 proxy源对象的响应对象的集合
   */
  proxyMap.set(target, proxy);
  // 将这个响应式对象返回
  return proxy;
}

export function isReactive(value: unknown): boolean {
  if (isReadonly(value)) {
    return isReactive((value as Target)[ReactiveFlags.RAW]);
  }
  return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE]);
}

export function isReadonly(value: unknown): boolean {
  return !!(value && (value as Target)[ReactiveFlags.IS_READONLY]);
}

export function isProxy(value: unknown): boolean {
  return isReactive(value) || isReadonly(value);
}

/**
 * 返回响应式对象的原始数据
 */

export function toRaw<T>(observed: T): T {
  const raw = observed && (observed as Target)[ReactiveFlags.RAW];
  return raw ? toRaw(raw) : observed;
}

export function markRaw<T extends object>(value: T): T {
  /**
   * 本质是设置这个对象的属性特征 讲它的 enumerable改为false
   *  这样就不会成为响应式对象了
   */
  def(value, ReactiveFlags.SKIP, true);
  return value;
}

export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value;

export const toReadonly = <T extends unknown>(value: T): T =>
  isObject(value) ? readonly(value as Record<any, any>) : value;
